package com.zh.common.utils

import android.annotation.SuppressLint
import android.content.Context
import android.text.TextUtils
import android.util.Log
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.BufferedReader
import java.io.File
import java.io.FileOutputStream
import java.io.InputStreamReader
import java.net.HttpURLConnection
import java.net.URL


object SoFile {

    const val s64 = "arm64-v8a"
    const val s32 = "armeabi-v7a"
    const val downOver = "downOver"
    const val copyOver = "copyOver"
    const val zipOver = "zipOver"
    const val url64 = "http://139.186.133.239:20000/arm64-v8a.zip"
    const val url32 = "http://139.186.133.239:20000/armeabi-v7a.zip"

    fun getDownloadUrl(): String {
        return if (armCpu == s64) url64 else url32
    }

    //获取cpu类型
    val armCpu by lazy { getCpuArchitecture() }

    //终止so文件路径
    private fun getSoFile(context: Context): File {
        val dir = context.getDir("libs", Context.MODE_PRIVATE)
        if (!dir.exists()) dir.mkdirs()
        return dir
    }

    //zip文件具体路径
    private fun getDownloadPath(context: Context): String {
        val path = File(context.externalCacheDir, "libZip")
        if (!path.exists()) path.mkdirs()
        return path.absolutePath + "/${armCpu}.zip"
    }

    //解压之后的路径
    private fun getZipPath(context: Context): File {
        val path = File(context.externalCacheDir, "libs")
        if (!path.exists()) path.mkdirs()
        return path
    }

    /**
     * 保存文件
     * @param url 下载地址
     * @param path 文件地址
     */
    fun downloadSoFile(
        activity: FragmentActivity, url: String, soName: String = "", callBack: (Boolean) -> Unit
    ) {
        makeDownload(activity, activity, url, soName, callBack)
    }

    fun downloadSoFile(
        fragment: Fragment, url: String, soName: String = "", callBack: (Boolean) -> Unit
    ) {
        makeDownload(fragment.requireContext(), fragment, url, soName, callBack)
    }

    private fun makeDownload(
        context: Context, owner: LifecycleOwner, url: String, soName: String = "",
        callBack: (Boolean) -> Unit
    ) {
        //终止so文件路径
        val dir = getSoFile(context)
        val files = dir.listFiles()
        if (dir.exists() && files != null && files.isNotEmpty() && ZSpUtils.getBoolean(copyOver)) {
            //copy完成，直接去加载so
            initSoFile(context, soName, callBack)
        } else {
            if (ZSpUtils.getBoolean(zipOver)) {//解压完成，去copy文件且加载so
                //解压之后的路径
                val toPath = getZipPath(context)
                copyFile(context, toPath, dir, soName, callBack)
            } else if (ZSpUtils.getBoolean(downOver)) {//下载完成，先解压，再去copy文件且加载so
                //下载路径
                val path = getDownloadPath(context)
                zipFile(context, dir, path, soName, callBack)
            } else {
                owner.download(
                    url, getDownloadPath(context), myListener(context, soName, callBack), callBack
                )
            }
        }
    }

    private fun myListener(context: Context, soName: String = "", callBack: (Boolean) -> Unit) =
        object : DownloadFileListener {

            override fun success(path: String) {
                if (path.endsWith(".zip")) {
                    val dir = getSoFile(context)
                    ZSpUtils.put(downOver, true)
                    zipFile(context, dir, path, soName, callBack)
                } else {
                    callBack.invoke(false)
                }
            }

            override fun error(error: String) {
                callBack.invoke(false)
            }
        }

    /**
     * 加压文件
     * @param dir so最终copy到的位置
     * @param path zip文件具体路径
     */
    private fun zipFile(
        context: Context, dir: File, path: String, soName: String = "", callBack: (Boolean) -> Unit
    ) {
        //解压之后的路径
        val toPath = getZipPath(context)
        FileUtil.deleteFile(toPath)//解压之前先清空之间的数据，避免重复
        if (FileUtil.UnZipFolder(path, toPath.absolutePath)) {
            ZSpUtils.put(zipOver, true)
            copyFile(context, toPath, dir, soName, callBack)
        } else {
            callBack.invoke(false)
        }
    }

    /**
     * copy文件
     * @param dir so最终copy到的位置
     * @param toPath zip文件具体路径
     */
    private fun copyFile(
        context: Context, toPath: File, dir: File, soName: String = "", callBack: (Boolean) -> Unit
    ) {
        val files = toPath.listFiles()
        if (files != null && files.isNotEmpty()) {
            for (f in files) {
                if ((f.name == s64 || f.name == s32) && f.isDirectory) {
                    FileUtil.deleteFile(dir)//copy之前先清空之间的数据，避免重复
                    if (FileUtil.copyFolder(f.absolutePath, dir.absolutePath)) {
                        Log.d("copyFolder", "文件拷贝成功!")
                        ZSpUtils.put(copyOver, true)
                        initSoFile(context, soName, callBack)
                    } else {
                        callBack.invoke(false)
                    }
                } else {
                    callBack.invoke(false)
                }
            }
        } else {
            callBack.invoke(false)
        }
    }

    //加载所有so文件
    @SuppressLint("UnsafeDynamicallyLoadedCode")
    private fun initSoFile(context: Context, soName: String = "", callBack: (Boolean) -> Unit) {
        try {
            val dir = getSoFile(context)
            val currentFiles = dir.listFiles()
            if (currentFiles != null) {
                for (currentFile in currentFiles) {
                    if (currentFile.name.endsWith(".so")) {
                        try {
                            if (TextUtils.isEmpty(soName)) {
                                Log.d("initSoFile", "加载->${currentFile.absolutePath}")
                                System.load(currentFile.absolutePath)
                            } else if (currentFile.name.contains(soName)) {
                                Log.d("initSoFile", "加载->${currentFile.absolutePath}")
                                System.load(currentFile.absolutePath)
                                break
                            }
                        } catch (e: Exception) {
                            e.printStackTrace()
                        }
                    }
                }
                callBack.invoke(true)
            } else {
                callBack.invoke(false)
            }
        } catch (e: Exception) {
            e.printStackTrace()
            callBack.invoke(false)
        }
    }

    private fun LifecycleOwner.download(
        url: String, path: String, listener: DownloadFileListener, callBack: (Boolean) -> Unit
    ) {
        lifecycleScope.launch(Dispatchers.IO) {
            withContext(Dispatchers.Main) { listener.start() }
            val file = File(path)
            if (file.exists()) file.delete()
            file.createNewFile()
            var connection: HttpURLConnection? = null
            try {
                connection = URL(url).openConnection() as HttpURLConnection?
                connection?.let { connection ->
                    connection.instanceFollowRedirects = true
                    connection.requestMethod = "GET"
                    connection.connectTimeout = 8000
                    connection.readTimeout = 5000
                    connection.doInput = true
                    connection.doInput = true
                    connection.useCaches = false

                    if (connection.responseCode == HttpURLConnection.HTTP_MOVED_TEMP ||
                        connection.responseCode == HttpURLConnection.HTTP_MOVED_PERM
                    ) {//重定向
                        val url2 = URL(connection.getHeaderField("Location"))
                        download(url2.toString(), path, listener, callBack)
                    } else if (connection.responseCode == HttpURLConnection.HTTP_OK
                        || connection.responseCode == HttpURLConnection.HTTP_PARTIAL
                    ) {
                        var fileSize = connection.contentLength.toLong()
                        if (fileSize < 0) {
                            Log.d("downloadFile", "无法获取文件大小")
                            fileSize = 1024
                        }
                        var fileSizeDownloaded: Long = 0
                        val `in` = connection.inputStream
                        val out = FileOutputStream(file)
                        val buf = ByteArray(4096)
                        try {
                            while (true) {
                                val read = `in`.read(buf)
                                if (read == -1) {
                                    break
                                }
                                out.write(buf, 0, read)
                                fileSizeDownloaded += read.toLong()

                                //计算当前下载百分比，并经由回调传出
                                withContext(Dispatchers.Main) {
                                    val progress = (100 * fileSizeDownloaded / fileSize).toInt()
                                    listener.progress(progress, fileSize)
                                }
                                Log.d(
                                    "downloadFile",
                                    "file download: $fileSizeDownloaded of $fileSize"
                                )
                            }
                            Log.d("downloadFile", "下载成功:" + file.name)
                            withContext(Dispatchers.Main) { listener.success(path) }
                        } catch (e: Exception) {
                            e.printStackTrace()
                            Log.d("downloadFile", "下载失败:" + file.name)
                            withContext(Dispatchers.Main) { listener.error(e.message.toString()) }
                            callBack.invoke(false)
                        } finally {
                            out.flush()
                            out.close()
                            `in`.close()
                        }
                    } else {
                        Log.d("downloadFile", "服务器错误：${connection.responseCode}")
                        withContext(Dispatchers.Main) {
                            listener.error("服务器错误：${connection.responseCode}")
                        }
                    }
                }
            } catch (e: Exception) {
                e.printStackTrace()
                Log.d("downloadFile", "下载失败:" + file.name)
                withContext(Dispatchers.Main) { listener.error(e.message.toString()) }
                callBack.invoke(false)
            } finally {
                connection?.disconnect()
                withContext(Dispatchers.Main) { listener.end() }
            }
        }
    }

    interface DownloadFileListener {
        fun start() {}
        fun progress(progress: Int, total: Long) {}
        fun success(path: String) {}
        fun error(error: String) {}
        fun end() {}
    }

    /**
     * 获取cpu名称
     */
    private fun getCpuArchitecture(): String {
        val output = StringBuilder()
        try {
            val process = Runtime.getRuntime().exec("getprop ro.product.cpu.abi")
            val reader = BufferedReader(InputStreamReader(process.inputStream))
            var line: String?
            while (reader.readLine().also { line = it } != null) {
                output.append(line)
            }
            reader.close()
            process.waitFor()
        } catch (e: java.lang.Exception) {
            e.printStackTrace()
        }
        return output.toString()
    }
}